#ifndef VERTEX_BUFFER_H
#define VERTEX_BUFFER_H

#include "Graphics/VertexAttributes.hpp"

/*  
  VertexBuffer.cpp
  ==============================================

  1) Declare vertex data structure:
        
  struct MyVertex 
  {
     Vec3f position,
     Vec2i material
  };
 
  2) Declare vertex attrib declaration class:
 
  class MyVertexDecl : public VertexDecl
  {
     MyVertexDecl()
     {
        AddElement(Position, GL_FLOAT, 3, 0);
        AddElement(Material, GL_UNSIGNED_BYTE, 2, 12);
     }
  }

  3) Declare VertexBuffer:
 
  VertexBuffer<MyVertex, MyVertexDecl> *myVBO;

  4) To fill buffer with data:

  myVBO->SetData(data);
  
  where data is of type std::vector<MyVertex>

  5) To draw buffer:

  myVBO->Enable();
  myVBO->EnableVertexArrays();
  
  *optional*
  myIBO->Enable();
  DrawElements/DrawArrays();

  myVBO->DisableVertexArrays();

  myIBO->Disable();
  myVBO->Disable();

*/

template <typename T, typename D = PositionTextureVertexDecl>
class VertexBuffer
{
public:
    
    VertexBuffer(): verticesNumber(0)
    {    
        glGenBuffers(1, &bufferId);
    }

    ~VertexBuffer()
    {
        glDeleteBuffers(1, &bufferId);
    }

    template <typename T>
    void SetData(std::vector<T> vertices)
    {

    }

    void SetData(std::vector<T> vertices)
    {
        // Clear buffer which is said to be faster than updating it    
        SetEmpty();
        glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(T), &vertices[0], GL_STATIC_DRAW);            
        verticesNumber = vertices.size();
    }

	void SetEmpty()
	{
		glBufferData(GL_ARRAY_BUFFER, 0, 0, GL_STATIC_DRAW);
		verticesNumber = 0;
	}

    void EnableVertexArrays()
    {
        D decl = D();

        // Skipping all default declarations so we don't crash
        if (decl.skip()) { return; }
        		
        std::vector<VertexElement> elements = decl.GetElements();        
        for (unsigned int i = 0; i < elements.size(); i++)
        {                        
            glEnableVertexAttribArray(elements[i].attr);		
            glVertexAttribPointer(elements[i].attr, elements[i].size, elements[i].type, GL_FALSE,
                sizeof(T), BUFFER_OFFSET(elements[i].offset));
        }
    }

	void DisableVertexArrays()
    {
        D decl = D();

        // Skipping all default declarations so we don't crash
        if (decl.skip()) { return; }
        
        std::vector<VertexElement> elements = decl.GetElements();        
        for (unsigned int i = 0; i < elements.size(); i++)
        {                        
            glDisableVertexAttribArray(elements[i].attr);
        }
    }

    void SetBufferId(GLuint buffer)
    {
        bufferId = buffer;
    }

    void Enable()
    {
        glBindBuffer(GL_ARRAY_BUFFER, bufferId);
    }

    void Disable()
    {
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    int GetVerticesNumber()
    {
        return verticesNumber;
    }
    
    private:
        GLuint bufferId;
        int verticesNumber;
};

#endif